import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:built_collection/built_collection.dart';
import 'package:mobile_app.core/collection_navigator/application/bloc/bloc.dart';
import 'package:mobile_app.core/collection_navigator/application/models/single_lexical_item_page_model.dart';
import 'package:mobile_app.core/core/application/models/app_navigator_transition_info.dart';
import 'package:mobile_app.core/core/domain/config/config.dart';
import 'package:mobile_app.core/core/domain/entities/dialect.dart';
import 'package:mobile_app.core/core/domain/entities/email_app.dart';
import 'package:mobile_app.core/core/domain/entities/learning_content/lexical_item/lexical_item.dart';
import 'package:mobile_app.core/core/domain/utils/either_extension.dart';
import 'package:mobile_app.core/email/application/bloc/bloc.dart';
import 'package:mobile_app.core/learning_content/application/bloc/bloc.dart';
import 'package:mobile_app.core/overlay/application/bloc/bloc.dart';
import 'package:mobile_app.core/purchase_flow_display/application/bloc.dart';
import 'package:mobile_app.core/settings/application/bloc/bloc.dart';
import 'package:mobile_app.core/url_launcher/application/bloc/bloc.dart';
import 'package:mobile_app.core/user_info/application/bloc/bloc.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/add_lexical_item_to_custom_collection/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/add_lexical_item_to_learning_queue/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/complete_custom_lexical_item_deletion/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/exclude_lexical_item_from_learning/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/get_initial_data/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/handle_available_email_app_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/handle_cached_image_path_map_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/handle_dialect_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/handle_image_settings_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/handle_learning_content_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/handle_premium_access_status_outer_update/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/initialize/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/mark_lexical_item_as_completely_learned/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/mark_lexical_item_as_known/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/pronounce_lexical_item/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/pronounce_lexical_item_use_case/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/remove_lexical_item_from_learning_queue/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/report_lexical_item_mistake_by_email/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/reset_lexical_item_progress/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/return_lexical_item_to_learning/use_case.dart';
import 'package:mobile_app.home.collections/single_lexical_item_page/domain/use_cases/start_custom_lexical_item_deletion/use_case.dart';
import 'package:mobile_app.home.core/core/domain/entities/card_actions/addition_to_collection_card_action.dart';
import 'package:mobile_app.home.core/core/domain/entities/card_actions/addition_to_learning_queue_card_action.dart';
import 'package:mobile_app.home.core/core/domain/entities/card_actions/card_action.dart';
import 'package:mobile_app.home.core/core/domain/entities/card_actions/exclusion_from_learning_card_action.dart';
import 'package:mobile_app.home.core/core/domain/entities/card_actions/mark_as_completely_learned_card_action.dart';
import 'package:mobile_app.home.core/core/domain/entities/card_actions/mark_as_known_card_action.dart';

part 'bloc_state.dart';

class SingleLexicalItemPageBloc extends Cubit<SingleLexicalItemPageBlocState> {
  SingleLexicalItemPageBloc({
    required SingleLexicalItemPageModel pageModel,
    required LearningContentBloc learningContentBloc,
    required UserInfoBloc userInfoBloc,
    required SettingsBloc settingsBloc,
    required OverlayBloc overlayBloc,
    required EmailBloc emailBloc,
    required UrlLauncherBloc urlLauncherBloc,
    required CollectionNavigatorBloc collectionNavigatorBloc,
    required PurchaseFlowDisplayBloc purchaseFlowDisplayBloc,
    required GetInitialData getInitialData,
    required Initialize initialize,
    required AddLexicalItemToCustomCollection addLexicalItemToCustomCollection,
    required AddLexicalItemToLearningQueue addLexicalItemToLearningQueue,
    required CompleteCustomLexicalItemDeletion completeCustomLexicalItemDeletion,
    required ExcludeLexicalItemFromLearning excludeLexicalItemFromLearning,
    required HandleAvailableEmailAppOuterUpdate handleAvailableEmailAppOuterUpdate,
    required HandleCachedImagePathMapOuterUpdate handleCachedImagePathMapOuterUpdate,
    required HandleDialectOuterUpdate handleDialectOuterUpdate,
    required HandleImageSettingsOuterUpdate handleImageSettingsOuterUpdate,
    required HandleLearningContentOuterUpdate handleLearningContentOuterUpdate,
    required HandlePremiumAccessStatusOuterUpdate handlePremiumAccessStatusOuterUpdate,
    required MarkLexicalItemAsCompletelyLearned markLexicalItemAsCompletelyLearned,
    required MarkLexicalItemAsKnown markLexicalItemAsKnown,
    required PronounceLexicalItem pronounceLexicalItem,
    required PronounceLexicalItemUseCase pronounceLexicalItemUseCase,
    required RemoveLexicalItemFromLearningQueue removeLexicalItemFromLearningQueue,
    required ReportLexicalItemMistakeByEmail reportLexicalItemMistakeByEmail,
    required ResetLexicalItemProgress resetLexicalItemProgress,
    required ReturnLexicalItemToLearning returnLexicalItemToLearning,
    required StartCustomLexicalItemDeletion startCustomLexicalItemDeletion,
  })  : _pageModel = pageModel,
        _learningContentBloc = learningContentBloc,
        _userInfoBloc = userInfoBloc,
        _settingsBloc = settingsBloc,
        _emailBloc = emailBloc,
        _overlayBloc = overlayBloc,
        _urlLauncherBloc = urlLauncherBloc,
        _collectionNavigatorBloc = collectionNavigatorBloc,
        _purchaseFlowDisplayBloc = purchaseFlowDisplayBloc,
        _addLexicalItemToCustomCollection = addLexicalItemToCustomCollection,
        _addLexicalItemToLearningQueue = addLexicalItemToLearningQueue,
        _completeCustomLexicalItemDeletion = completeCustomLexicalItemDeletion,
        _excludeLexicalItemFromLearning = excludeLexicalItemFromLearning,
        _handleAvailableEmailAppOuterUpdate = handleAvailableEmailAppOuterUpdate,
        _handleCachedImagePathMapOuterUpdate = handleCachedImagePathMapOuterUpdate,
        _handleDialectOuterUpdate = handleDialectOuterUpdate,
        _handleImageSettingsOuterUpdate = handleImageSettingsOuterUpdate,
        _handleLearningContentOuterUpdate = handleLearningContentOuterUpdate,
        _handlePremiumAccessStatusOuterUpdate = handlePremiumAccessStatusOuterUpdate,
        _markLexicalItemAsCompletelyLearned = markLexicalItemAsCompletelyLearned,
        _markLexicalItemAsKnown = markLexicalItemAsKnown,
        _pronounceLexicalItem = pronounceLexicalItem,
        _pronounceLexicalItemUseCase = pronounceLexicalItemUseCase,
        _removeLexicalItemFromLearningQueue = removeLexicalItemFromLearningQueue,
        _reportLexicalItemMistakeByEmail = reportLexicalItemMistakeByEmail,
        _resetLexicalItemProgress = resetLexicalItemProgress,
        _returnLexicalItemToLearning = returnLexicalItemToLearning,
        _startCustomLexicalItemDeletion = startCustomLexicalItemDeletion,
        super(_getInitialState(
          pageModel: pageModel,
          learningContentBlocState: learningContentBloc.state!,
          userInfoBlocState: userInfoBloc.state!,
          settingsBlocState: settingsBloc.state!,
          emailBlocState: emailBloc.state!,
          getInitialData: getInitialData,
        )) {
    final initializationResult = initialize(
      lexicalItem: state.lexicalItem,
    );

    _cachedImagePathMapStreamSubscription =
        initializationResult.cachedImagePathMapStream.listen(_onCachedImagePathMapChanged);

    _learningContentBlocSubscription =
        learningContentBloc.stream.listen(_onLearningContentBlocStateChanged);

    _settingsBlocSubscription = settingsBloc.streamWithPrevious.listen(_onSettingsBlocStateChanged);

    _emailBlocSubscription = emailBloc.stream.listen(_onEmailBlocStateChanged);

    _userInfoBlocSubscription = userInfoBloc.streamWithPrevious.listen(_onUserInfoBlocStateChanged);
  }

  final SingleLexicalItemPageModel _pageModel;
  final LearningContentBloc _learningContentBloc;
  final UserInfoBloc _userInfoBloc;
  final SettingsBloc _settingsBloc;
  final EmailBloc _emailBloc;
  final OverlayBloc _overlayBloc;
  final UrlLauncherBloc _urlLauncherBloc;
  final CollectionNavigatorBloc _collectionNavigatorBloc;
  final PurchaseFlowDisplayBloc _purchaseFlowDisplayBloc;

  final AddLexicalItemToCustomCollection _addLexicalItemToCustomCollection;
  final AddLexicalItemToLearningQueue _addLexicalItemToLearningQueue;
  final CompleteCustomLexicalItemDeletion _completeCustomLexicalItemDeletion;
  final ExcludeLexicalItemFromLearning _excludeLexicalItemFromLearning;
  final HandleAvailableEmailAppOuterUpdate _handleAvailableEmailAppOuterUpdate;
  final HandleCachedImagePathMapOuterUpdate _handleCachedImagePathMapOuterUpdate;
  final HandleDialectOuterUpdate _handleDialectOuterUpdate;
  final HandleImageSettingsOuterUpdate _handleImageSettingsOuterUpdate;
  final HandleLearningContentOuterUpdate _handleLearningContentOuterUpdate;
  final HandlePremiumAccessStatusOuterUpdate _handlePremiumAccessStatusOuterUpdate;
  final MarkLexicalItemAsCompletelyLearned _markLexicalItemAsCompletelyLearned;
  final MarkLexicalItemAsKnown _markLexicalItemAsKnown;
  final PronounceLexicalItem _pronounceLexicalItem;
  final PronounceLexicalItemUseCase _pronounceLexicalItemUseCase;
  final RemoveLexicalItemFromLearningQueue _removeLexicalItemFromLearningQueue;
  final ReportLexicalItemMistakeByEmail _reportLexicalItemMistakeByEmail;
  final ResetLexicalItemProgress _resetLexicalItemProgress;
  final ReturnLexicalItemToLearning _returnLexicalItemToLearning;
  final StartCustomLexicalItemDeletion _startCustomLexicalItemDeletion;

  late final StreamSubscription<BuiltMap<String, String>> _cachedImagePathMapStreamSubscription;
  late final StreamSubscription<LearningContentBlocState> _learningContentBlocSubscription;
  late final StreamSubscription<(UserInfoBlocState, UserInfoBlocState?)> _userInfoBlocSubscription;
  late final StreamSubscription<(SettingsBlocState, SettingsBlocState?)> _settingsBlocSubscription;
  late final StreamSubscription<EmailBlocState> _emailBlocSubscription;

  static SingleLexicalItemPageBlocState _getInitialState({
    required SingleLexicalItemPageModel pageModel,
    required LearningContentBlocState learningContentBlocState,
    required UserInfoBlocState userInfoBlocState,
    required SettingsBlocState settingsBlocState,
    required EmailBlocState emailBlocState,
    required GetInitialData getInitialData,
  }) {
    final dialect = settingsBlocState.pronunciationSettings.dialect;
    final lexicalItemMap = learningContentBlocState.lexicalItemMap;
    final collectionListItems = learningContentBlocState.collectionListItems;
    final availableEmailApps = emailBlocState.availableEmailApps;

    final result = getInitialData(
      lexicalItemId: pageModel.lexicalItemId,
      imageSettings: settingsBlocState.imageSettings,
      lexicalItemMap: lexicalItemMap,
      collectionListItems: collectionListItems,
      unsafeToPronounceLexicalItemTitles:
          learningContentBlocState.unsafeToPronounceLexicalItemTitleMap,
      availableEmailApps: availableEmailApps,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
      dialect: dialect,
    );

    return SingleLexicalItemPageBlocState(
      lexicalItem: result.lexicalItem,
      imagePath: result.imagePath,
      dialect: dialect,
      safeToPronounce: result.safeToPronounce,
      cardActions: result.cardActions,
      lexicalItemExplicitlyDeleted: false,
    );
  }

  void _onCachedImagePathMapChanged(BuiltMap<String, String> cachedImagePathMap) {
    if (state.lexicalItemExplicitlyDeleted) {
      return;
    }

    final result = _handleCachedImagePathMapOuterUpdate(
      lexicalItem: state.lexicalItem,
      imageSettings: _settingsBloc.state!.imageSettings,
      cachedImagePathMap: cachedImagePathMap,
    );

    if (state.imagePath == result.imagePath) {
      return;
    }

    emit(state.copyWith(
      imagePath: () => result.imagePath,
    ));
  }

  void _onLearningContentBlocStateChanged(LearningContentBlocState learningContentBlocState) {
    if (state.lexicalItemExplicitlyDeleted) {
      return;
    }

    final failureOrResult = _handleLearningContentOuterUpdate(
      lexicalItemId: _pageModel.lexicalItemId,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
      availableEmailApps: _emailBloc.state!.availableEmailApps,
      collectionListItems: learningContentBlocState.collectionListItems,
      lexicalItemMap: learningContentBlocState.lexicalItemMap,
      dialect: _settingsBloc.state!.pronunciationSettings.dialect,
      unsafeToPronounceLexicalItemTitleMap:
          _learningContentBloc.state!.unsafeToPronounceLexicalItemTitleMap,
    );

    if (failureOrResult.isLeft()) {
      final failure = failureOrResult.asLeft();

      switch (failure) {
        case NonexistentLexicalItemLearningContentOuterUpdateHandlingFailure():
          _forceClosePage();
      }

      return;
    }

    final result = failureOrResult.asRight();

    emit(state.copyWith(
      lexicalItem: () => result.lexicalItem,
      cardActions: () => result.cardActions,
      safeToPronounce: () => result.safeToPronounce,
    ));
  }

  void _onUserInfoBlocStateChanged((UserInfoBlocState, UserInfoBlocState?) valueWithPrevious) {
    final (userInfoBlocState, userInfoBlocPreviousState) = valueWithPrevious;

    if (state.lexicalItemExplicitlyDeleted) {
      return;
    }

    if (userInfoBlocState.premiumAccessStatus == userInfoBlocPreviousState!.premiumAccessStatus) {
      return;
    }

    final result = _handlePremiumAccessStatusOuterUpdate(
      lexicalItem: state.lexicalItem,
      premiumAccessStatus: userInfoBlocState.premiumAccessStatus,
      availableEmailApps: _emailBloc.state!.availableEmailApps,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      cardActions: () => result.cardActions,
    ));
  }

  void _onSettingsBlocStateChanged((SettingsBlocState, SettingsBlocState?) valueWithPrevious) {
    final (settingsBlocState, settingsBlocPreviousState) = valueWithPrevious;

    if (state.lexicalItemExplicitlyDeleted) {
      return;
    }

    var updatedState = state;

    final dialect = settingsBlocState.pronunciationSettings.dialect;

    if (state.dialect != dialect) {
      final result = _handleDialectOuterUpdate(
        lexicalItem: state.lexicalItem,
        dialect: dialect,
        unsafeToPronounceLexicalItemTitleMap:
            _learningContentBloc.state!.unsafeToPronounceLexicalItemTitleMap,
      );

      updatedState = updatedState.copyWith(
        dialect: () => dialect,
        safeToPronounce: () => result.safeToPronounce,
      );
    }

    if (settingsBlocState.imageSettings != settingsBlocPreviousState!.imageSettings) {
      final result = _handleImageSettingsOuterUpdate(
        lexicalItem: state.lexicalItem,
        imageSettings: settingsBlocState.imageSettings,
      );

      updatedState = updatedState.copyWith(
        imagePath: () => result.imagePath,
      );
    }

    emit(updatedState);
  }

  void _onEmailBlocStateChanged(EmailBlocState emailBlocState) {
    if (state.lexicalItemExplicitlyDeleted) {
      return;
    }

    final result = _handleAvailableEmailAppOuterUpdate(
      lexicalItem: state.lexicalItem,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
      availableEmailApps: emailBlocState.availableEmailApps,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      cardActions: () => result.cardActions,
    ));
  }

  void _forceClosePage() {
    const transitionInfo = RemovalNavigatorTransitionInfo(
      hasAnimation: false,
    );

    _collectionNavigatorBloc.closePage(
      pageModel: _pageModel,
      transitionInfo: transitionInfo,
    );
  }

  void requestLexicalItemPronunciation() {
    _pronounceLexicalItem(
      lexicalItemTitle: state.lexicalItem.title,
    );
  }

  void requestLexicalItemUseCasePronunciation({
    required LexicalItemUseCase lexicalItemUseCase,
  }) {
    _pronounceLexicalItemUseCase(
      lexicalItemUseCase: lexicalItemUseCase,
    );
  }

  void requestMistakeReportThroughTelegram() {
    _urlLauncherBloc.launch(
      schemaUrl: Config().telegramChannelMistakeReportMessageSchemaUrl,
      browserUrl: Config().telegramChannelMistakeReportMessageBrowserUrl,
    );
  }

  void requestMistakeReportByEmail({
    required EmailApp? emailApp,
  }) {
    _reportLexicalItemMistakeByEmail(
      lexicalItem: state.lexicalItem,
      pronunciationSettings: _settingsBloc.state!.pronunciationSettings,
      emailApp: emailApp,
    );
  }

  void requestProgressReset() {
    final result = _resetLexicalItemProgress(
      lexicalItem: state.lexicalItem,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
      availableEmailApps: _emailBloc.state!.availableEmailApps,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      lexicalItem: () => result.lexicalItem,
      cardActions: () => result.cardActions,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestMarkLexicalItemAsKnown() {
    final cardAction = state.cardActions.firstWhere((cardAction) {
      return cardAction is MarkAsKnownCardAction;
    }) as MarkAsKnownCardAction;

    if (!cardAction.permitted) {
      _purchaseFlowDisplayBloc.openPurchasePage();
      return;
    }

    final result = _markLexicalItemAsKnown(
      lexicalItem: state.lexicalItem,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
      availableEmailApps: _emailBloc.state!.availableEmailApps,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      lexicalItem: () => result.lexicalItem,
      cardActions: () => result.cardActions,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestMarkLexicalItemAsCompletelyLearned() {
    final cardAction = state.cardActions.firstWhere((cardAction) {
      return cardAction is MarkAsCompletelyLearnedCardAction;
    }) as MarkAsCompletelyLearnedCardAction;

    if (!cardAction.permitted) {
      _purchaseFlowDisplayBloc.openPurchasePage();
      return;
    }

    final result = _markLexicalItemAsCompletelyLearned(
      lexicalItem: state.lexicalItem,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
      availableEmailApps: _emailBloc.state!.availableEmailApps,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      lexicalItem: () => result.lexicalItem,
      cardActions: () => result.cardActions,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestLexicalItemAdditionToLearningQueue() {
    final cardAction = state.cardActions.firstWhere((cardAction) {
      return cardAction is AdditionToLearningQueueCardAction;
    }) as AdditionToLearningQueueCardAction;

    if (!cardAction.permitted) {
      _purchaseFlowDisplayBloc.openPurchasePage();
      return;
    }

    final result = _addLexicalItemToLearningQueue(
      lexicalItem: state.lexicalItem,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
      availableEmailApps: _emailBloc.state!.availableEmailApps,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      lexicalItem: () => result.lexicalItem,
      cardActions: () => result.cardActions,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestLexicalItemRemovalFromLearningQueue() {
    final result = _removeLexicalItemFromLearningQueue(
      lexicalItem: state.lexicalItem,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
      availableEmailApps: _emailBloc.state!.availableEmailApps,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      lexicalItem: () => result.lexicalItem,
      cardActions: () => result.cardActions,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestLexicalItemReturnToLearning() {
    final result = _returnLexicalItemToLearning(
      lexicalItem: state.lexicalItem,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
      availableEmailApps: _emailBloc.state!.availableEmailApps,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      lexicalItem: () => result.lexicalItem,
      cardActions: () => result.cardActions,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestLexicalItemAdditionToCustomCollection({
    required String customCollectionId,
  }) {
    final cardAction = state.cardActions.firstWhere((cardAction) {
      return cardAction is AdditionToCustomCollectionCardAction;
    }) as AdditionToCustomCollectionCardAction;

    if (!cardAction.permitted) {
      _purchaseFlowDisplayBloc.openPurchasePage();
      return;
    }

    final result = _addLexicalItemToCustomCollection(
      lexicalItem: state.lexicalItem,
      customCollectionId: customCollectionId,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
      availableEmailApps: _emailBloc.state!.availableEmailApps,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      cardActions: () => result.cardActions,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      collectionListItems: () => result.collectionListItems,
    );
  }

  void requestLexicalItemExclusionFromLearning() {
    final cardAction = state.cardActions.firstWhere((cardAction) {
      return cardAction is ExclusionFromLearningCardAction;
    }) as ExclusionFromLearningCardAction;

    if (!cardAction.permitted) {
      _purchaseFlowDisplayBloc.openPurchasePage();
      return;
    }

    final result = _excludeLexicalItemFromLearning(
      lexicalItem: state.lexicalItem,
      premiumAccessStatus: _userInfoBloc.state!.premiumAccessStatus,
      availableEmailApps: _emailBloc.state!.availableEmailApps,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      lexicalItem: () => result.lexicalItem,
      cardActions: () => result.cardActions,
    ));

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
    );
  }

  void requestLexicalItemDeletionStart() {
    final result = _startCustomLexicalItemDeletion(
      lexicalItemId: _pageModel.lexicalItemId,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
    );

    if (result.isLexicalItemUsedInAnyCollection) {
      emit(state.copyWith(
        usedCustomLexicalItemDeletionDialogShown: () => true,
      ));
    } else {
      emit(state.copyWith(
        orphanCustomLexicalItemDeletionDialogShown: () => true,
      ));
    }
  }

  void requestLexicalItemDeletionCompletion() {
    final result = _completeCustomLexicalItemDeletion(
      lexicalItemId: _pageModel.lexicalItemId,
      collectionListItems: _learningContentBloc.state!.collectionListItems,
      lexicalItemMap: _learningContentBloc.state!.lexicalItemMap,
    );

    emit(state.copyWith(
      lexicalItemExplicitlyDeleted: () => true,
    ));

    _collectionNavigatorBloc.handlePop();

    _overlayBloc.showSuccessIndicator();

    _learningContentBloc.updateWith(
      lexicalItemMap: () => result.lexicalItemMap,
      collectionListItems: () => result.collectionListItems,
    );
  }

  @override
  Future<void> close() {
    _cachedImagePathMapStreamSubscription.cancel();
    _learningContentBlocSubscription.cancel();
    _userInfoBlocSubscription.cancel();
    _settingsBlocSubscription.cancel();
    _emailBlocSubscription.cancel();
    return super.close();
  }
}
